package com.ftg.learn.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.List;

/**
 * @author 布衣
 */
@Configuration
@ConfigurationProperties(prefix = "spring.data")
@Data
@Slf4j
public class RedisClusterConfig {

    //主数据
    private RedisServerInstanceConfig master;

    //所有从数据
    private List<RedisServerInstanceConfig> slave;

    /**
     * 主redis有连接工厂
     *
     * @return LettuceConnectionFactory
     */
    @Bean
    @Primary
    public LettuceConnectionFactory masterFactory() {
        return new LettuceConnectionFactory(master.getHost(), master.getPort());
    }

    /**
     * 主redis模板类实例呀
     *
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<String, String> masterRedisTemplate() {
        RedisTemplate<String, String> masterRedis = new RedisTemplate<>();

        //工厂
        masterRedis.setConnectionFactory(masterFactory());

        //序列化问题
        masterRedis.setKeySerializer(new StringRedisSerializer());
        masterRedis.setHashKeySerializer(new StringRedisSerializer());

        masterRedis.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        masterRedis.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        masterRedis.afterPropertiesSet();
        return masterRedis;
    }


    /**
     * 负载均衡器算法，用于获取可用的从服务器
     *
     * @return
     */
    @Bean
    @Scope("prototype")
    public RedisTemplate<String, String> slaveRedisTemplate() {
        RedisTemplate<String, String> slaveRedis = new RedisTemplate<>();

        //工厂
        slaveRedis.setConnectionFactory(slaveFactory());

        //序列化问题
        slaveRedis.setKeySerializer(new StringRedisSerializer());
        slaveRedis.setHashKeySerializer(new StringRedisSerializer());

        slaveRedis.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        slaveRedis.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        slaveRedis.afterPropertiesSet();
        return slaveRedis;
    }

    @Bean
    @Scope("prototype")
    public LettuceConnectionFactory slaveFactory() {

        for (int i = 0; i < slave.size(); i++) {
            // 轮询选择从服务器
            RedisServerInstanceConfig slaveConfig = getSlaveByPolling();

            try {
                // 创建从服务器连接
                LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(
                        slaveConfig.getHost(), slaveConfig.getPort());
                if (isConnectionAvailable(lettuceConnectionFactory)) {
                    return lettuceConnectionFactory;
                }
            } catch (Exception e1) {
                log.error("从redis连接失败 Exception ：主机{}：port{}, 错误: {}",
                        slaveConfig.getHost(), slaveConfig.getPort(), e1.getMessage());
            }
        }
        // 所有从服务器都连接失败，回退到主服务器
        try {
            LettuceConnectionFactory fallbackConnectionFactory = new LettuceConnectionFactory(
                    master.getHost(), master.getPort());
            return fallbackConnectionFactory;
        } catch (Exception fallbackException) {
            log.error("主服务器也连接失败 {}:{}, 错误: {}",
                    master.getHost(), master.getPort(), fallbackException.getMessage());
            throw new RuntimeException("无法连接到任何Redis服务器", fallbackException);
        }
    }

    private static int index = 0;

    /**
     * 负载均衡算法一 ： 轮询
     *
     * @return RedisServerInstanceConfig
     */
    private RedisServerInstanceConfig getSlaveByPolling() {
        int size = RedisClusterConfig.index++ % slave.size();
        return slave.get(size);
    }

    /**
     * 负载均衡算法一 ： 随机
     *
     * @return RedisServerInstanceConfig
     */
    private RedisServerInstanceConfig getSlaveByRandom() {
        return slave.get((int) (Math.random() * slave.size()));
    }

    private boolean isConnectionAvailable(LettuceConnectionFactory connectionFactory) {
        try {
            // 初始化连接工厂
            connectionFactory.afterPropertiesSet();
            // 启动连接工厂
            connectionFactory.start();
            // 获取连接并测试
            var connection = connectionFactory.getConnection();
            // 执行一个简单的命令来测试连接
            connection.ping();
            // 关闭连接
            connection.close();
            return true;
        } catch (Exception e) {
            log.error("连接测试失败: {}", e.getMessage());
            return false;
        } catch (Throwable t) {
            log.error("连接测试发生严重错误: {}", t.getMessage());
            return false;
        }
    }

}
